package io.shuttle.mbe.api

import io.shuttle.mbe.api.annotation.ChromeMinVersion
import io.shuttle.mbe.api.annotation.PromiseStyleMinVersion
import io.shuttle.mbe.api.types.Value1Function
import io.shuttle.mbe.core.Promise

////////////////////
// Cookies
////////////////////
/**
 * Use the `Browser.cookies` API to query and modify cookies, and to be notified when they change.
 *
 * Permissions: "cookies"
 *
 * Manifest: "host_permissions"
 */
interface Cookies {
    // Retrieves information about a single cookie.
    // If more than one cookie of the same name exists for the given URL,
    // the one with the longest path will be returned.
    // For cookies with the same path length,
    // the cookie with the earliest creation time will be returned.
    @PromiseStyleMinVersion(88)
    fun get(
        details: CookieDetails,
        callback: Value1Function<Cookie>? = null
    ): Promise<Cookie?>

    // Retrieves all cookies from a single cookie store that match the given information.
    // The cookies returned will be sorted, with those with the longest path first.
    // If multiple cookies have the same path length, those with the earliest creation time will be first.
    // This method only retrieves cookies for domains that the extension has host permissions to.
    @PromiseStyleMinVersion(88)
    fun getAll(
        details: GetAllDetails,
        callback: Value1Function<List<Cookie>>? = null
    ): Promise<List<Cookie>>

    // Lists all existing cookie stores.
    @PromiseStyleMinVersion(88)
    fun getAllCookieStores(
        callback: Value1Function<List<CookieStore>>? = null
    ): Promise<List<CookieStore>>

    @ChromeMinVersion(132)
    @PromiseStyleMinVersion(88)
    fun getPartitionKey(
        details: FrameDetails,
        callback: Value1Function<List<PartitionKeyReturn>>? = null
    ): Promise<List<PartitionKeyReturn>>

    // Deletes a cookie by name.
    @PromiseStyleMinVersion(88)
    fun remove(
        details: CookieDetails,
        callback: Value1Function<CookieDetails>? = null
    ): Promise<CookieDetails?>

    // Sets a cookie with the given cookie data; may overwrite equivalent cookies if they exist.
    @PromiseStyleMinVersion(88)
    fun set(
        details: Cookie,
        callback: Value1Function<Cookie>? = null
    ): Promise<Cookie?>

    // Fired when a cookie is set or removed. As a special case,
    // note that updating a cookie's properties is implemented as a two step process:
    // the cookie to be updated is first removed entirely, generating a notification with "cause" of "overwrite" .
    // Afterwards, a new cookie is written with the updated values, generating a second notification with "cause" "explicit".
    val onChanged: Events.Event<Value1Function<CookieChangeInfo>>


    // Details to identify the cookie.
    @ChromeMinVersion(88)
    data class CookieDetails(
        // The name of the cookie to access.
        var name: String,
        // The partition key for reading or modifying cookies with the Partitioned attribute.
        @ChromeMinVersion(119)
        var partitionKey: CookiePartitionKey? = null,
        // The ID of the cookie store in which to look for the cookie. By default,
        // the current execution context's cookie store will be used.
        var storeId: String? = null,
        // The URL with which the cookie to access is associated.
        // This argument may be a full URL, in which case any data following the URL path
        // (e.g. the query string) is simply ignored.
        // If host permissions for this URL are not specified in the manifest file, the API call will fail.
        var url: String,
    )

    data class GetAllDetails(
        // Restricts the retrieved cookies to those whose domains match or are subdomains of this one.
        var domain: String?,
        // Filters the cookies by name.
        var name: String?,
        // The partition key for reading or modifying cookies with the Partitioned attribute.
        @ChromeMinVersion(119)
        var partitionKey: CookiePartitionKey?,
        // Restricts the retrieved cookies to those whose path exactly matches this string.
        var path: String?,
        // Filters the cookies by their Secure property.
        var secure: Boolean?,
        // Filters out session vs. persistent cookies.
        var session: Boolean?,
        // The cookie store to retrieve cookies from. If omitted, the current execution context's cookie store will be used.
        var storeId: String?,
        // Restricts the retrieved cookies to those that would match the given URL.
        var url: String?
    )

    // Represents a partitioned cookie's partition key.
    @ChromeMinVersion(119)
    data class CookiePartitionKey(
        // Indicates if the cookie was set in a cross-cross site context. This prevents a top-level
        // site embedded in a cross-site context from accessing cookies set by the top-level site in a same-site context.
        @ChromeMinVersion(130)
        var hasCrossSiteAncestor: Boolean? = null,
        // The top-level site the partitioned cookie is available in.
        var topLevelSite: String? = null,
    )

    // Contains details about the partition key that's been retrieved.
    data class PartitionKeyReturn(
        // The partition key for reading or modifying cookies with the Partitioned attribute.
        var partitionKey: CookiePartitionKey
    )

    // Represents a cookie store in the browser. An incognito mode window, for instance,
    // uses a separate cookie store from a non-incognito window.
    data class CookieStore(
        // The unique identifier for the cookie store.
        var id: String,
        // Identifiers of all the browser tabs that share this cookie store.
        var tabIds: List<Number>,
    )

    // Details to identify the frame.
    @ChromeMinVersion(132)
    data class FrameDetails(
        // The unique identifier for the document. If the frameId and/or tabId are provided they
        // will be validated to match the document found by provided document ID.
        var documentId: String? = null,
        // The unique identifier for the frame within the tab.
        var frameId: Number? = null,
        // The unique identifier for the tab containing the frame.
        var tabId: Number? = null,
    )

    // The underlying reason behind the cookie's change. If a cookie was inserted,
    // or removed via an explicit call to "chrome.cookies.remove", "cause" will be "explicit".
    // If a cookie was automatically removed due to expiry, "cause" will be "expired".
    // If a cookie was removed due to being overwritten with an already-expired expiration date,
    // "cause" will be set to "expired_overwrite".
    // If a cookie was automatically removed due to garbage collection, "cause" will be "evicted".
    // If a cookie was automatically removed due to a "set" call that overwrote it, "cause" will be "overwrite".
    // Plan your response accordingly.
    @ChromeMinVersion(44)
    enum class OnChangedCause {
        evicted,
        expired,
        explicit,
        expired_overwrite,
        overwrite,
    }

    // A cookie's 'SameSite' state (https://tools.ietf.org/html/draft-west-first-party-cookies).
    // 'no_restriction' corresponds to a cookie set with 'SameSite=None', 'lax' to 'SameSite=Lax',
    // and 'strict' to 'SameSite=Strict'. 'unspecified' corresponds to a cookie set without the SameSite attribute.
    @ChromeMinVersion(51)
    enum class SameSiteStatus {
        no_restriction,
        lax,
        strict,
        unspecified
    }

    data class CookieChangeInfo(
        // The underlying reason behind the cookie's change.
        var cause: OnChangedCause,
        // Information about the cookie that was set or removed.
        var cookie: Cookie,
        // True if a cookie was removed.
        var removed: Boolean
    )
}

// Represents information about an HTTP cookie.
data class Cookie(
    // Represents information about an HTTP cookie.
    val domain: String,

    // The expiration date of the cookie as the number of seconds since the UNIX epoch.
    // Not provided for session cookies.
    val expirationDate: Number?,

    // True if the cookie is a host-only cookie (i.e. a request's host must exactly match the domain of the cookie).
    val hostOnly: Boolean,

    // True if the cookie is marked as HttpOnly (i.e. the cookie is inaccessible to client-side scripts).
    val httpOnly: Boolean,

    // The name of the cookie.
    val name: String,

    // The partition key for reading or modifying cookies with the Partitioned attribute.
    @ChromeMinVersion(119)
    val partitionKey: Cookies.CookiePartitionKey?,

    // The path of the cookie.
    val path: String,

    // The cookie's same-site status (i.e. whether the cookie is sent with cross-site requests).
    @ChromeMinVersion(51)
    val sameSite: Cookies.SameSiteStatus,

    // True if the cookie is marked as Secure (i.e. its scope is limited to secure channels, typically HTTPS).
    val secure: Boolean,

    // True if the cookie is a session cookie, as opposed to a persistent cookie with an expiration date.
    val session: Boolean,

    // The ID of the cookie store containing this cookie, as provided in getAllCookieStores().
    val storeId: String,

    // The value of the cookie.
    val value: String

)